package com.liao.search.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggest;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import com.liao.common.doc.ProductDoc;
import com.liao.common.dto.ProductSearchByCategoryRequest;
import com.liao.common.dto.ProductSearchRequest;
import com.liao.common.vo.SearchResult;
import com.liao.search.constant.SortConstant;
import com.liao.search.service.HistoryKeywordSaveSaveService;
import com.liao.search.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static com.liao.common.constant.EsMappingEnum.PRODUCT_INDEX;

/**
 * @Author: ljt
 * @Description:
 * @DateTime: 2025/3/12 下午8:22
 **/
@Slf4j
@Service
public class ProductServiceImpl implements ProductService {
    @Autowired
    private ElasticsearchClient elasticsearchClient;

    @Autowired
    private HistoryKeywordSaveSaveService historyKeywordSaveSaveService;


    //上传商品数据
    @Override
    public boolean productStatusUp(List<ProductDoc> productDoc) throws IOException {

        BulkRequest.Builder br = new BulkRequest.Builder();

        for (ProductDoc product : productDoc) {
            br.operations(op -> op
                    .index(idx -> idx
                            .index(PRODUCT_INDEX)
                            .id(String.valueOf(product.getProductId()))
                            .document(product)
                    )
            );
        }

        BulkResponse result = elasticsearchClient.bulk(br.build());

        // Log errors, if any
        if (result.errors()) {
            log.error("ES,商品上架错误");
            for (BulkResponseItem item : result.items()) {
                if (item.error() != null) {
                    log.error(item.error().reason());
                }
            }
            return false;

        }

        return true;
    }

    /**
     * es远程搜索数据
     *
     * @param searchRequest 请求参数
     * @return
     * @throws IOException todo 解决根据分类查询商品问题
     */
    @Override
    public SearchResult<ProductDoc> search(ProductSearchRequest searchRequest) throws IOException {

        SearchResult<ProductDoc> result = new SearchResult<>();
        Integer from = searchRequest.getFrom();
        Integer size = searchRequest.getSize();
        String text = searchRequest.getText();
        String sortField = searchRequest.getSortField();
        String sortMethod = searchRequest.getSortMethod();

        result.setPageNum(from);
        result.setPageSize(size);
        // 1. 构建查询请求数据
        SearchRequest.Builder requestBuilder = new SearchRequest.Builder()
                .index(PRODUCT_INDEX)
//                前端传递默认是1开始，es以0开始
                .from(from == 0 ? 0 : from - 1)
                .size(size)
                .query(q -> q
                        .match(m -> m
                                .field("all")
                                .query(v -> v.stringValue(text))
                        )
                );
//如果请求的字段符合标准则添加进请求数据中
        if (SortConstant.FieldS.contains(sortField) && SortConstant.Methods.contains(sortMethod)) {
            requestBuilder.sort(so -> so
                    .field(f -> f
                            .field(sortField)
                            .order(SortOrder.Asc.toString().equalsIgnoreCase(sortMethod) ? SortOrder.Asc : SortOrder.Desc)
                            .missing("_last")
                    )
            );
        }

//2.构建请求
        SearchRequest request = requestBuilder.build();

        // 3. 执行查询
        SearchResponse<ProductDoc> response = elasticsearchClient.search(request, ProductDoc.class);

        List<ProductDoc> productDocList = new ArrayList<>();

        // 4. 处理结果
        // 获取已转换的对象
        for (Hit<ProductDoc> hit : response.hits().hits()) {
            ProductDoc productDoc = hit.source();
            productDocList.add(productDoc);
        }
        long total = response.hits().total().value();
        result.setTotal(total);
        result.setTotalPage((long) Math.ceil((double) total / size));
        result.setItems(productDocList);

        return result;
    }

    /**
     * 获取用户推荐热门产品
     *
     * @param memberId 用户id
     * @param from     从第几条开始
     * @param size     长度
     * @return
     * @throws IOException
     */
    @Override
    public SearchResult<ProductDoc> recommendProducts(Integer memberId, Integer from, Integer size) throws IOException {

        //判断用户是否登录，未登录返回随机数据
        if (memberId == 0 || memberId == null) {
            SearchResult<ProductDoc> searchResult = radomProducts(10);
            searchResult.setPageNum(from);

            return searchResult;
        }


//        1. 获取用户常搜关键词
        String memberAlwaysKey = historyKeywordSaveSaveService.getMemberAlwaysKey(memberId);
        // 2. 根据用户常搜关键词搜索

        ProductSearchRequest searchRequest = new ProductSearchRequest();
        searchRequest.setFrom(from);
        searchRequest.setSize(size);
        searchRequest.setText(memberAlwaysKey);
        SearchResult<ProductDoc> search = search(searchRequest);

        return search;
    }

    /**
     * 随机获取产品
     *
     * @param size 长度
     * @return
     * @throws IOException
     */
    @Override
    public SearchResult<ProductDoc> radomProducts(Integer size) throws IOException {
        SearchResult<ProductDoc> result = new SearchResult<>();
        result.setPageSize(size);
        //1. 构建随机查询
        SearchRequest request = SearchRequest.of(s -> s
                .index(PRODUCT_INDEX)
                .size(size)
                .query(q -> q
                        .scriptScore(ss -> ss
                                .query(ssQ -> ssQ.matchAll(m -> m))
                                .script(sc -> sc
                                        .inline(i -> i
                                                .source("Math.random()")  // 生成随机数
                                        )
                                )
                        )
                )
        );
        // 2. 执行查询
        SearchResponse<ProductDoc> response = elasticsearchClient.search(request, ProductDoc.class);

        List<ProductDoc> productDocList = new ArrayList<>();

        // 3. 处理结果
        response.hits().hits().forEach(hit -> {
            System.out.println(hit.source());
            productDocList.add(hit.source());
        });
        long total = response.hits().total().value();
        result.setTotal(total);
        result.setTotalPage((long) Math.ceil((double) total / size));
        result.setItems(productDocList);
        return result;

    }

    @Override
    public List<String> getTips(String keyword) throws IOException {
        String suggest = "key-suggest"; //自定义建议器名称
        String SearchFiled = "title.suggest";//目标字段（需是 completion 类型）

        SearchRequest request = SearchRequest.of(s -> s
                        .index(PRODUCT_INDEX)
                        .suggest(sg -> sg                      // 建议查询入口
                                        .suggesters(suggest, sb -> sb  // 自定义建议器名称
                                                        .prefix(keyword)            // 搜索前缀词
                                                        .completion(c -> c              // 补全建议配置
                                                                        .field(SearchFiled)     // 目标字段（需是 completion 类型）
//                                        .skipDuplicates(true)       // 跳过重复项
//                                        .size(10)                   // 返回建议数量
                                                        )
                                        )
                        )
        );

        // 2. 执行查询
        SearchResponse<Object> response = elasticsearchClient.search(request, Object.class);

// 获取建议结果（新版返回的是 Map 的替代结构）
        List<String> list = new ArrayList<>();
        List<Suggestion<Object>> suggestList = response.suggest().get(suggest);

        if (suggestList != null) {
            for (Suggestion<Object> suggestion : suggestList) {
                if (suggestion.isCompletion()) {
                    CompletionSuggest<Object> completion = suggestion.completion();
                    for (CompletionSuggestOption<Object> option : completion.options()) {
                        String text = option.text();
                        list.add(text);
//                        System.out.println(text);
                    }
                }
            }
        }


        return list;
    }

    @Override
    public List<ProductDoc> getRelevant(String id) throws IOException {
        Integer size = 8;

//1.查询获取当前商品信息
        ProductDoc productDoc = getProductDoc(id);


        String title = productDoc.getTitle();
        String productId = productDoc.getProductId().toString(); //获取当前商品id productId == 文档id

//        2.构建查询请求
        SearchRequest request = SearchRequest.of(s -> s
                .index(PRODUCT_INDEX)
                .size(size) // 限制结果数量
                .query(q -> q
                        .bool(b -> b
                                .must(m -> m
                                        .match(mt -> mt
                                                .field("all")
                                                .query(title)
                                        )
                                )
                                .mustNot(mn -> mn
                                        .ids(ids -> ids
                                                .values(productId) // 支持单个或多个值
                                        )
                                )
                        )
                )
        );
        SearchResponse<ProductDoc> response = elasticsearchClient.search(request, ProductDoc.class);

        // 4. 获取总命中数
        long totalHits = response.hits().total().value();
        System.out.println("总匹配数: " + totalHits);

        // 5. 提取文档数据
        List<ProductDoc> products = response.hits().hits().stream()
                .map(Hit::source) // 提取文档对象
                .collect(Collectors.toList());


        return products;
    }

    @Override
    public SearchResult<ProductDoc> searchProductBycategoryId(ProductSearchByCategoryRequest searchRequest) throws IOException {
        SearchResult<ProductDoc> result = new SearchResult<>();
        Integer from = searchRequest.getFrom();
        Integer size = searchRequest.getSize();
        String categoryId = searchRequest.getCategoryId();
        String sortField = searchRequest.getSortField();
        String sortMethod = searchRequest.getSortMethod();

        result.setPageNum(from);
        result.setPageSize(size);
        // 1. 构建查询请求数据
        SearchRequest.Builder requestBuilder = new SearchRequest.Builder()
                .index(PRODUCT_INDEX)
//                前端传递默认是1开始，es以0开始
                .from(from == 0 ? 0 : from - 1)
                .size(size)
                .query(q -> q
                        .match(m -> m
                                .field("category_id")
                                .query(v -> v.stringValue(categoryId))
                        )
                );
//如果请求的字段符合标准则添加进请求数据中
        if (SortConstant.FieldS.contains(sortField) && SortConstant.Methods.contains(sortMethod)) {
            requestBuilder.sort(so -> so
                    .field(f -> f
                            .field(sortField)
                            .order(SortOrder.Asc.toString().equalsIgnoreCase(sortMethod) ? SortOrder.Asc : SortOrder.Desc)
                            .missing("_last")
                    )
            );
        }

//2.构建请求
        SearchRequest request = requestBuilder.build();

        // 3. 执行查询
        SearchResponse<ProductDoc> response = elasticsearchClient.search(request, ProductDoc.class);

        List<ProductDoc> productDocList = new ArrayList<>();

        // 4. 处理结果
        // 获取已转换的对象
        for (Hit<ProductDoc> hit : response.hits().hits()) {
            ProductDoc productDoc = hit.source();
            productDocList.add(productDoc);
        }
        long total = response.hits().total().value();
        result.setTotal(total);
        result.setTotalPage((long) Math.ceil((double) total / size));
        result.setItems(productDocList);

        return result;
    }


    /**
     * 根据文档id查询商品数据
     *
     * @param id
     */
    private ProductDoc getProductDoc(String id) throws IOException {
        // 1. 构建 Get 请求（更高效）
        GetRequest getRequest = GetRequest.of(g -> g
                .index("product")
                .id(id)
        );
        // 2. 执行请求
        GetResponse<ProductDoc> response = elasticsearchClient.get(getRequest, ProductDoc.class);

// 3. 处理结果
        if (!response.found()) {
            System.out.println("文档不存在");
            return null;
        }

        return response.source();
    }


}